using System;
using System.Collections.Generic;
using System.ServiceModel;
using System.Threading;
using SLS.ExClassLib.Events;
using SLS.ExClassLib.HLASimulation.Federation;
using SLS.ExClassLib.Message;
using SLS.ExClassLib.RTIServiceContract;
namespace LanSunStudio.Service.RTIService
{
    /// <summary>
    /// Beer Service Implementation
    /// </summary>
    /// <remarks>
    /// Single is the default concurrency mode, but it never hurts to be explicit.
    /// Note the single threading model still functions properly with use of one way methods for callbacks 
    /// since they are marked as one way on the contract.
    /// </remarks>
    [ServiceBehavior(
        ConcurrencyMode = ConcurrencyMode.Single,
        InstanceContextMode = InstanceContextMode.PerCall)]
    public partial class RTIService : IRTIServiceContract
    {
        #region =================Variable=================
        private static Dictionary<string, IRTIServiceContractCallBack> AgentCallbackList =
            new Dictionary<string, IRTIServiceContractCallBack>();
        public ExMessagePool MessagePool;
        public static AutoResetEvent waitHandle;
        public RTIService()
        {
            MessagePool = new ExMessagePool();
            waitHandle = new AutoResetEvent(false);
            try
            {

            }
            catch (Exception ex)
            {
                Tracer.Error(ex.StackTrace, ex);
            }
        }

        #endregion

        #region =================IRTIServiceContract---------
        void IRTIServiceContract.SendMAMessage(MessageEx msg)
        {
            try
            {
                MessageEx msgB = null;
                MessagePool.Add(msg);
                string JobID = msg.JobID;
                switch (JobID)
                {
                    case "OnJoinFederation":
                        RegisterAgent(msg);
                        msgB = MessagePool.Reply(msg.MessageID, msg.Federation, "OnJoinedFederation", "NO");
                        msgB = GetRTITimeStamp(msgB);
                        SendMessage(msgB.Address, msgB);
                        break;
                    default:
                        //BroadcastMessage(msg);
                        Tracer.Verbose(this.ToString() + "SendMAMessage: Unknown Job is received.", msg);
                        break;
                }
            }
            catch (Exception ex)
            {
                Tracer.Error(ex.StackTrace, ex);
            }
        }
        string IRTIServiceContract.Subscribe()
        {
            IRTIServiceContractCallBack callback =
                OperationContext.Current.GetCallbackChannel<IRTIServiceContractCallBack>();
            string m_AgentPath = Guid.NewGuid().ToString().ToUpper();
            try
            {
                if (callback != null)
                {
                    lock (AgentCallbackList)
                    {
                        AgentCallbackList.Add(m_AgentPath, callback);
                    }
                }
            }
            catch (Exception ex)
            {
                Tracer.Error(ex.StackTrace, ex);
            }
            return m_AgentPath;
        }
        void IRTIServiceContract.Unsubscribe(string m_AgentPath)
        {
            lock (AgentCallbackList)
            {
                if (AgentCallbackList.ContainsKey(m_AgentPath))
                {
                    AgentCallbackList.Remove(m_AgentPath);
                }
            }
        }
        void IRTIServiceContract.KeepConnection()
        {
            // Do nothing.
        }
        public MessageEx GetRTITimeStamp(MessageEx msg)
        {
            //m_DesFederateName is the memeber of the federation
            DateTime timestamp = DateTime.Now;
            try
            {
                timestamp = DateTime.Now;
                string update = (string)msg.Tag;
                if (update == "YES")
                {
                    RTIMatrix.Instance.UpdateFederateTime(msg.Address, msg.Federation, timestamp);
                    Tracer.Verbose("RTI:" + msg.Address+" : "+msg.JobID, " is being passing the message" + msg.JobID + " on " + timestamp);
                }
                msg.TimeStamp = timestamp;
            }
            catch (Exception ex)
            {
                Tracer.Error(ex.StackTrace, ex);
            }
            return msg;
        }
        public MessageEx GetFEDMinTimeStamp(MessageEx msg)
        {
            DateTime timestamp = DateTime.Now;
            try
            {
                timestamp = RTIMatrix.Instance.GetMinFederateTime(msg.Address, msg.Federation);
                msg.TimeStamp = timestamp;
            }
            catch (Exception ex)
            {
                Tracer.Error(ex.StackTrace, ex);
            }
            return msg;
        }
        public MessageEx GetFEDMaxTimeStamp(MessageEx msg)
        {
            DateTime timestamp = DateTime.Now;
            try
            {
                timestamp = RTIMatrix.Instance.GetMaxFederateTime(msg.Address, msg.Federation);
                msg.TimeStamp = timestamp;
            }
            catch (Exception ex)
            {
                Tracer.Error(ex.StackTrace, ex);
            }
            return msg;
        }
        public MessageEx RegulateFEDCurrentTime(MessageEx msg)
        {
            DateTime timestamp = DateTime.Now;
            try
            {
                timestamp = RTIMatrix.Instance.RegulateFederateCurrentTime(msg.Address, msg.Federation, msg.TimeStamp);
                msg.TimeStamp = timestamp;
            }
            catch (Exception ex)
            {
                Tracer.Error(ex.StackTrace, ex);
            }
            return msg;
        }
        public MessageEx GetMessgeOnWallTimeTable(MessageEx msg)
        {
            int msgnum = 0;
            try
            {
                Tracer.Verbose("GetMessgeOnWallTimeTable", msg.JobID);
                msgnum = RTIMatrix.Instance.GetMessgeOnWallTimeTable(msg.Address, msg.Federation, msg.TimeStamp, (int)msg.Tag);
                msg.Tag = msgnum;
            }
            catch (Exception ex)
            {
                Tracer.Error(ex.StackTrace, ex);
            }
            return msg;
        }
        public void DelMessgeOnWallTimeTable(MessageEx msg)
        {

            try
            {
                RTIMatrix.Instance.DelMessgeOnWallTimeTable(msg.Address, msg.Federation, msg.TimeStamp);
            }
            catch (Exception ex)
            {
                Tracer.Error(ex.StackTrace, ex);
            }
        }

        #endregion

        #region=================TOOLS=================
        public void RegisterAgent(MessageEx msg)
        {
            IRTIServiceContractCallBack callback =
                OperationContext.Current.GetCallbackChannel<IRTIServiceContractCallBack>();
            string m_AgentPath = msg.Address;
            try
            {
                if (callback != null)
                {
                    lock (AgentCallbackList)
                    {
                        if (!AgentCallbackList.ContainsKey(m_AgentPath))
                        {
                            AgentCallbackList.Add(m_AgentPath, callback);
                        }
                    }
                }
                RTIMatrix.Instance.UpdateFederateTime(msg.Address, msg.Federation, msg.TimeStamp, false);
            }
            catch (Exception ex)
            {
                Tracer.Error(ex.StackTrace, ex);
            }
        }
        private void BroadcastMessage(MessageEx msgRequest)
        {
            // Call each client's callback method
            string m_AgentPath = msgRequest.Address;
            ThreadPool.QueueUserWorkItem
                (
                delegate
                {
                    lock (AgentCallbackList)
                    {
                        List<string> disconnectedClientGuids = new List<string>();
                        foreach (KeyValuePair<string, IRTIServiceContractCallBack> client in AgentCallbackList)
                        {
                            try
                            {
                                client.Value.HandleMAMessage(msgRequest);
                            }
                            catch (Exception)
                            {
                                disconnectedClientGuids.Add(client.Key);
                            }
                        }
                        foreach (string clientGuid in disconnectedClientGuids)
                        {
                            AgentCallbackList.Remove(clientGuid);
                        }
                    }
                }
            );
        }
        public void RegisterMessage(MessageEx msg)
        {
            MessagePool.Add(msg);
        }
        private void SendMessage(string AgentPath, MessageEx msg)
        {
            try
            {
                msg.Address = AgentPath;
                IRTIServiceContractCallBack AgentCallback =
                    (IRTIServiceContractCallBack)AgentCallbackList[AgentPath];
                AgentCallback.HandleMAMessage(msg);
            }
            catch (Exception ex)
            {
                Tracer.Error(ex.StackTrace, ex);
            }
        }
        #endregion

    }

}
